【製造業 IoTも簡単可視化!】AWS IoT SiteWise に収集した設備機器の稼働データを Amazon Managed Grafana で可視化してみた
AWS IoT SiteWise は SiteWiseモニターという可視化ツールで簡単に収集したデータを可視化することができますが、サポートしているグラフ形式が少なかったり、あまり複雑な可視化ができません。
一方で、Amazon Managed Grafana は SiteWise をデータソースとしてサポートしているので、スピードメーターのようなビジュアルである「ゲージ表示」など様々な形で SiteWise のデータを可視化することができます。
今回は、Managed Grafana で簡単なダッシュボードの作り方までをご紹介したいと思います。
前提
今回は SiteWise のデータは API を使ってダミーデータを投入しています。
ダミーデータを投入したコードは本記事の末尾に記載しましたので必要に応じてご利用いただければと思います。
Amazon Managed Grafanaワークスペースの作成
まず最初に、Amazon Managed Grafana のワークスペースを作ります。
適当なワークスペース名と説明を記入して「次へ」をクリックします。
今回は、認証に Auth0 で SAML 認証を使うので「認証アクセス」は「SAML」を選択します。
「アクセス許可タイプ」は「サービスマネージド」を選択して「次へ」進みます。
アカウントの指定では、「現在のアカウント」を選択して、データソースには「AWS IoT SiteWise」を選択します。
レビュー画面の内容で問題なければ「ワークスペースを作成」をクリックします。
作成できたら、ワークスペースの画面に遷移するので「セットアップを完了にする」をクリックして SAML の設定を行います。
なお、ここでは Auth0 側の作業は割愛いたします。詳細は下記のブログの通りですのでご参考にしていただければと思います。
Auth0 の作業と並行して、下記の「ステップ1」〜「ステップ3」の作業を完了させます。
「ステップ1」では Auth0 に設定する 3つの URL を確認しておきます。
「ステップ2」では、Auth0 からダウンロードした メタデータの XML ファイルをアップロードします。
「ステップ3」では Auth0 側の設定に合わせた内容をそれぞれセットします。
「管理者ロールの値」では、Auth0 で複数ユーザーが存在して、それらも管理者としたい場合にカンマ区切りで登録することもできます。
管理者は、Grafana の画面でダッシュボードを作ったり変更することが可能です。
Amazon Managed Grafana へログイン
設定が完了したら「Grafana ワークスペース URL」にアクセスしてログインします。
「Sign in with SAML」をクリックします。
ログイン画面では Auth0 の設定に基づき認証情報を入れてログインします。
Grafana のデータソースを設定する
無事にログインできたら、早速設定していきましょう。 メニューから「AWS のアイコン」から「Data Sources」をクリックします。
データソースの設定では、それぞれ次のように指定します。
- Service:IoT SiteWise
- Default Region:Asia Pacific (Tokyo)
選択できたら「Add data source」をクリックします。
「Add data source」をクリックすると、画面の下に追加したデータソースが出現するので、「Go to settings」をクリックしてデータソースの設定画面を開きます。
ここではそのまま「Save & test」をクリックして設定を保存します。
ダッシュボードを作ってみる
次にダッシュボードを作ります。
メニューの「+」から「Dashboard」をクリックします。
次の画面で「Add a new panel」をクリックします。
下記はパネル追加直後の画面です。プロットされているグラフはダミーなので無視して構いません。
「Data source」で先程設定したデータソースである「AWS IoT SiteWise」を選択します。
次にクエリタイプとプロットしたいアセットを指定します。今回は次のようにしました。
- Query type:Get property value history
- Property Alias:
/test/device/1/temperature
今回は直接プロパティエイリアスを指定しましたが、下の図のように Grafana の画面から対象のアセットとプロパティを選択して設定することも可能です。
今回はデータとして2つのアセットがあるので、1つ目のアセットの設定が終わったら同じように2つ目も「+Query」ボタンを押して追加で設定します。
余談ですが、設定したクエリの名前も好きなものに編集することもできます。
先程と同じように2つ目のアセットに対しても設定ができました。
最後に「Title」を編集して適当なものに変更しておきます。
ここまでできたら「Apply」を押して設定内容を適用しましょう。
保存した設定でグラフが描画される様になりました。ここでは「描画期間」と「グラフの更新間隔」をそれぞれ「過去5分」、「5秒で更新」としています。
これで一通りの作業が完了したので「保存」ボタンをクリックして保存しましょう。
適当なダッシュボード名を付けて保存します。
毎度同じようなサンプルになりますが、今回はとりあえず下記のようなグラフの作成までとしました。
最後に
SiteWise データの可視化として、これまで次のようなことをやってきました。
- AWS IoT SiteWise モニターで可視化
- Amazon QuickSight で可視化
- Google データポータルで可視化
「簡単さ」という観点では、SiteWise モニターが最も簡単に感じますが、「簡単であり様々な可視化表現ができる」という点では Grafana がとても使いやすいと感じました。
(なにげにタイムゾーンが簡単にプルダウンから変更できるのは嬉しいですね)
一方で、認証が「AWS IAM Identity Center(旧 AWS SSO)」と「SAML」のみのサポートである点が導入のネックになりそうに感じました。
SiteWIse モニターのように IAM による認証もサポートされると、導入のハードルが更に下がるのではと思いました。今後のアップデートに期待しましょう。
これからもっと使っていきたいと思います。
おまけ:SiteWise API でダミーデータを送るスクリプト
本記事で可視化に使ったデータを生成する Python スクリプトです。一定時間ごとに値の高低を繰り返します。
import boto3 import uuid import random import time import logging import sys import json import math import threading logger = logging.getLogger() logger.setLevel(logging.INFO) streamHandler = logging.StreamHandler(stream=sys.stdout) formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') streamHandler.setFormatter(formatter) logger.addHandler(streamHandler) client = boto3.client('iotsitewise') def batch_put_asset(device_id, sending_period): start = time.time() mode = 'low' # 最初はLOWモードでスタートすることにする logger.info("starting low mode... ") while True: timer = int(time.time() - start) # 現在時刻の取得 (小数部, 整数部) timestamp_float, timestamp_int = math.modf(time.time()) if mode == 'low': if timer < sending_period: MeasureValueTemp = random.uniform(1, 30) MeasureValueClock = random.randint(600000000,800000000) #print('mode low') logger.info("temperature: {}".format(MeasureValueTemp)) logger.info("clock: {}".format(MeasureValueClock)) else: mode = 'high' logger.info("starting high mode... ") start = time.time() + 1 # reset timer continue else: #if mode == 'high': if timer < sending_period: # Mode: High load MeasureValueTemp = random.uniform(50, 90) MeasureValueClock = random.randint(1200000000,1500000000) #print('mode high') logger.info("temperature: {}".format(MeasureValueTemp)) logger.info("clock: {}".format(MeasureValueClock)) else: mode = 'low' logger.info("starting low mode... ") #print(str(int(time.time())) + ' start low mode...') start = time.time() + 1 # reset timer continue try: response = client.batch_put_asset_property_value( entries=[ { 'entryId': '{}'.format(uuid.uuid4()), 'propertyAlias': '/test/device/{}/temperature'.format(device_id), 'propertyValues': [ { 'value': { 'doubleValue': MeasureValueTemp }, 'timestamp': { 'timeInSeconds': int(timestamp_int), 'offsetInNanos': int(round((timestamp_float * 1000000000), 0)) }, 'quality': 'GOOD' }, ] }, { 'entryId': '{}'.format(uuid.uuid4()), 'propertyAlias': '/test/device/{}/clock'.format(device_id), 'propertyValues': [ { 'value': { 'doubleValue': MeasureValueClock }, 'timestamp': { 'timeInSeconds': int(timestamp_int), 'offsetInNanos': int(round((timestamp_float * 1000000000), 0)) }, 'quality': 'GOOD' }, ] }, ] ) logger.info("response: {}\n".format(json.dumps(response, indent=2))) logger.info("propertyAlias: {}\n".format(device_id)) if response['errorEntries']: logger.error("temperature: {} clock: {}".format(MeasureValueTemp, MeasureValueClock)) #else: # logger.info("temperature: {} clock: {}".format(MeasureValueTemp, MeasureValueClock)) except Exception as e: logger.error("{}".format(e)) logger.error("temperature: {} clock: {}".format(MeasureValueTemp, MeasureValueClock)) time.sleep(5) # args: デバイスID,負荷の高低の継続時間 device_1 = threading.Thread(target=batch_put_asset,args=(1,120)) device_2 = threading.Thread(target=batch_put_asset,args=(2,60)) device_1.start() device_2.start()